/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pymetadata.hh"
#include "pymetadata_gc.hh"
#include "pymetadata_global.hh"
#include "pymetadata_container.hh"
#include "pymetadata_vector.hh"
#include "string.hh"
#include "gendata_common.hh"
#include "marshalling.hh"

#include <Python.h>
#include <structmember.h>
#include <dlfcn.h>

// DEFINITIONS
#define INC_TP_GETSET             8 // power of 2
#define INC_TP_METHODS            8 // power of 2
#define ROUND_UP(n)               (((n)+3)&(~3))

#define PYMD_PROTO_DEFINITION(i)  PyObject *python_type_metadata::pyfunc##i(PyObject *self, PyObject *args) \
                                                                    {return pyfunc_resolver(i, self, args);}
#define PYMD_PROTO_INITIALIZAT(i) python_type_metadata::pyfunc##i,

#define OFFSET_ANY                0x40000000
#define OFFSET(class, member)     ((int)(&((class*)OFFSET_ANY)->member)-OFFSET_ANY)

#define MAX_PYMD_BUFFER           1024

#define INC_RENAME                8

// attribute names
#define ATTR_READ_ONLY            "ro"
#define ATTR_READ_ONLY_ALIAS1     "read_only"
#define ATTR_PREPICKLE            "prepickle"
#define ATTR_POSTUNPICKLE         "postunpickle"
#define ATTR_EVENT_QUEUE          "event_queue"
#define ATTR_NOPICKLE             "nopickle"
#define ATTR_PRIVATE              "private"
#define ATTR_NOWRAP               "nowrap"

// TYPEDEFS
enum python_internal_special_type
{
    PYMD_PYTHON_INTERNAL_SPECIAL_NONE,
    PYMD_PYTHON_INTERNAL_SPECIAL_UNARY,
    PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,
    PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, // for inplace binary operators
    PYMD_PYTHON_INTERNAL_SPECIAL_TERNARY,
    PYMD_PYTHON_INTERNAL_SPECIAL_INTARG,
    PYMD_PYTHON_INTERNAL_SPECIAL_LAST,
};

// GETSET CLOSURE
struct pymd_getset_closure
{
    union
    {
	int *data;
	int value;  // used for constants inside classes
    };
    pymd_attr_member attr;
};

// METHOD CLOSURE
struct pymd_method_closure
{
    struct _poly_
    {
	int *data;
	void *func;
	int vtable_pos;
	pymd_attr_method attr;
    } *poly;
    int n_poly;

    // special internal methods
    python_internal_special_type internal_type:8;
    unsigned internal_i_struct:8;
    unsigned internal_offset:16;
};


// STATIC MEMBERS
python_type_metadata *python_type_metadata::functions_object_ctx=NULL;

CLASS_INIT(python_type_metadata); // PYTHON_TYPE_METADATA CLASS

python_type_metadata::python_type_metadata(char *module_name, int *data_, char *doc_, void *dl)
{
    CLASS_CTOR(python_type_metadata);

    // rename
    util::ctor_vector(rename, n_rename, max_rename);

    // copy metadata
    data=data_;
    doc=doc_;

    // init python type object
    memset(&to, 0, sizeof(PyTypeObject));
    PyTypeObject dummy={PyObject_HEAD_INIT((PyTypeObject*)NULL)};
    memcpy(&to, &dummy, sizeof(PyObject));

    int i;
    char *class_name=gendata_get_string(data, doc, GENDATA_NAME);
    char *namespc=gendata_get_string(data, doc, GENDATA_NAMESPACE);
    to.tp_name=create_name(class_name, namespc, module_name);
    to.tp_basicsize=sizeof(python_metadata);
    to.tp_flags=Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES/*allow multitype operators*/;
    to.tp_doc=gendata_get_string(data, doc, GENDATA_DOC);

    to.tp_new=pynew;
    to.tp_init=pyinit;
    to.tp_dealloc=pydealloc;
    to.tp_members=(PyMemberDef *)calloc(1, sizeof(PyMemberDef));
    // its not necesary to use a tp_compare: to.tp_compare=pycompare;

    // reset ctor/dtor
    i_ctor=-1;
    dtor=NULL;

    // vtable
    char *vtable_link;
    if((vtable_link=gendata_get_string(data, doc, GENDATA_VTABLE_LINKAGE)))
    {
	vtable_position=(void**)dlsym(dl, vtable_link);
	if(vtable_position) vtable_position+=2;
    }
    else
	vtable_position=NULL;

    // first method
    method_closure_first=0;

    // reference count
    refcnt_location=GENDATA_INVALID_VALUE;

    // own metadata type reference
    data[gendata_get_position(data, GENDATA_GENDATA_RESERVE)]=(int)this;

    // number of metadata descriptions in class/struct
    int n_tp_methods, max_tp_methods;
    int n_tp_getset, max_tp_getset;
    int n_method_closure, max_method_closure;
    util::ctor_vector(to.tp_methods, n_tp_methods, max_tp_methods);
    util::ctor_vector(to.tp_getset, n_tp_getset, max_tp_getset);
    util::ctor_vector(method_closure, n_method_closure, max_method_closure);
    int n_meta=gendata_get_data(data, GENDATA_GENDATA_VAR);
    int *position=data+gendata_get_position(data, GENDATA_GENDATA_VAR)+1;
    int n_inheritance=0;
    for(i=0; i<n_meta; i++)
    {
	switch(*position)
	{
	case GENDATA_INHERITANCE:
	    // just prevent multiple inheritance at this point (actual inheritance is done in add() function)
	    if(++n_inheritance>1) fprintf(stderr, "internal: Multiple inheritance detected, forbid in previous stages\n");
	    break;
	case GENDATA_CLASS:
	case GENDATA_ENUM:
	    // embedded class/enum
	    break;
	case GENDATA_MEMBER:
	case GENDATA_MEMBER_VECTOR_N_MAX:
	case GENDATA_MEMBER_OBJECT:
	case GENDATA_MEMBER_STD_VECTOR_IMPL:
	case GENDATA_VARIABLE:
	{
	    // new getsetter
	    int i_getset=util::inc_vector(to.tp_getset, n_tp_getset, max_tp_getset, INC_TP_GETSET);
	    PyGetSetDef &getset=to.tp_getset[i_getset];

	    // new closure
	    pymd_getset_closure *closure=(pymd_getset_closure *)malloc(sizeof(pymd_getset_closure));
	    closure->data=position;
	    closure->attr=parse_attr_member_string(gendata_get_string(position, doc, GENDATA_ATTR));

	    getset.closure=closure;
	    getset.name=gendata_get_string(position, doc, GENDATA_NAME);
	    getset.doc=gendata_get_string(position, doc, GENDATA_DOC);

	    // private
	    if(closure->attr.priv) getset.name=rename_to_private(getset.name);

	    char *linkage; void *addr;
	    switch(*position)
	    {
	    case GENDATA_MEMBER:
		getset.get=pyget_member;
		getset.set=pyset_member;
		break;
	    case GENDATA_MEMBER_VECTOR_N_MAX:
		getset.get=pyget_member_vector_n_max;
		getset.set=pyset_member_vector_n_max;
		break;
	    case GENDATA_MEMBER_OBJECT:
		getset.get=pyget_member_object;
		getset.set=pyset_member_object;
		break;
	    case GENDATA_MEMBER_STD_VECTOR_IMPL:
		getset.get=pyget_member_std_vector_impl;
		getset.set=pyset_member_std_vector_impl;
		break;
	    case GENDATA_VARIABLE:
		getset.get=pyget_member_static;
		getset.set=pyset_member_static;
		if((linkage=gendata_get_string(position, doc, GENDATA_LINKAGE)) &&
		   (addr=dlsym(dl, linkage)))
		    position[gendata_get_position(position, GENDATA_GENDATA_RESERVE)]=(int)addr;
		break;
	    }
	    break;
	}
	case GENDATA_METHOD:
	    add_method(position, dl,
		       n_tp_methods, max_tp_methods,
		       n_method_closure, max_method_closure);
	    break;
	case GENDATA_REFERENCE_COUNT:
	    refcnt_location=gendata_get_data(position, GENDATA_LOCATION);
	    break;
	default:
	    fprintf(stderr, "internal: TODO: metadata_type %d at %d\n", *position, i);
	}
	position=gendata_traverse(position);
    }

    // internal methods
    PyMethodDef *methods;
    struct internal_str
    {
	const char *name;
	PyCFunction func;
    } internal[]=
      {
	  {"__getstate__", (PyCFunction)pygetstate},
	  {"__setstate__", (PyCFunction)pysetstate},
	  {"__getinitargs__", (PyCFunction)pygetinitargs},
	  {"__reduce__", (PyCFunction)pyreduce},
	  {"__reduce_ex__", (PyCFunction)pyreduce_ex},
	  {"__getmdattribute__", (PyCFunction)pygetmdattribute},
	  {"__debug__", (PyCFunction)pydebug},
	  {"__debug_ex__", (PyCFunction)pydebug_ex},
	  {"__debug_global__", (PyCFunction)pydebug_global},
	  {"__debug_internal__", (PyCFunction)pydebug_internal},
	  {"__debug_gc__", (PyCFunction)pydebug_gc},
	  {NULL, NULL}
      };

    for(int j=0; internal[j].name; j++)
    {
	int k;
	for(k=0; k<n_tp_methods; k++) if(!strcmp(to.tp_methods[k].ml_name, internal[j].name)) break;
	if(k<n_tp_methods) continue;

	i=inc_methods(n_tp_methods, max_tp_methods, n_method_closure, max_method_closure);
	methods=&to.tp_methods[i];
	methods->ml_name=const_cast<char *>(internal[j].name);
	methods->ml_doc=NULL;
	methods->ml_meth=internal[j].func;
	methods->ml_flags=METH_VARARGS;
    }

    // null terminated
    centinel_method(n_tp_methods, max_tp_methods);
    i=util::inc_vector(to.tp_getset, n_tp_getset, max_tp_getset, INC_TP_GETSET);
    memset(&to.tp_getset[i], 0, sizeof(PyGetSetDef));
}

python_type_metadata::~python_type_metadata()
{
    CLASS_DTOR(python_type_metadata);

    if(to.tp_name) free(const_cast<char*>(to.tp_name));
    if(to.tp_members) free(to.tp_members);
    if(to.tp_getset)
    {
	for(int i=0; to.tp_getset[i].name; i++)
	    if(to.tp_getset[i].closure) free(to.tp_getset[i].closure);
	free(to.tp_getset);
    }
    int i;
    for(i=0; to.tp_methods[i].ml_name; i++)
	if(method_closure[i].poly) free(method_closure[i].poly);
    if(to.tp_methods) free(to.tp_methods);
    // both of these are freed by python
    //if(to.tp_as_number) free(to.tp_as_number);
    //if(to.tp_as_sequence) free(to.tp_as_sequence);
    if(method_closure) free(method_closure);

    // rename
    util::dtor_vector_delete(rename, n_rename, max_rename);
}

int python_type_metadata::add_method(int *position, void *dl,
				     int &n_tp_methods, int &max_tp_methods,
				     int &n_method_closure, int &max_method_closure)
{
    // name of method
    char *name=gendata_get_string(position, doc, GENDATA_NAME);
    if(!name) {fprintf(stderr, "internal: Method w/o name\n"); return 0;}

    // linkage -> symbol -> entry point
    char *linkage=gendata_get_string(position, doc, GENDATA_LINKAGE);
    if(!linkage) {fprintf(stderr, "internal: Method {%s} w/o linkage\n", name); return 0;}
    void *func=dlsym(dl, linkage);
    if(!func) {fprintf(stderr, "internal: Cannot find symbol for {%s}, linkage {%s}\n", name, linkage); return 0;}

    // dtor
    if(*name=='~')
    {
	if(dtor) fprintf(stderr, "internal: More than one dtor in a class!? (%s)\n", name);
	dtor=func;
	return 1; // not callable under python!
    }

    // attributes
    pymd_attr_method attr=parse_attr_method_string(gendata_get_string(position, doc, GENDATA_ATTR));

    // map operator names
    char *python_name=NULL;
    python_internal_special_type python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_NONE;
    int python_internal_offset=0, python_internal_i_struct=-1;
    int n_params=gendata_get_data(position, GENDATA_GENDATA_VAR);
    if(!strncmp(name, "operator", 8))
    {
	// its an operator
	struct
	{
	    const char *c_name, *python_name;
	    int n_c_params;
	    python_internal_special_type type;
	    int i_struct, offset; // 0: PyNumberMethods  1: PySequenceMethods
	} operators[]=
	      {
		  // C/C++ unary operators
		  {"+",   "__pos__",      0, PYMD_PYTHON_INTERNAL_SPECIAL_UNARY,   0, OFFSET(PyNumberMethods, nb_positive)},
		  {"-",   "__neg__",      0, PYMD_PYTHON_INTERNAL_SPECIAL_UNARY,   0, OFFSET(PyNumberMethods, nb_negative)},
		  {"~",   "__invert__",   0, PYMD_PYTHON_INTERNAL_SPECIAL_UNARY,   0, OFFSET(PyNumberMethods, nb_invert)},
		  {"!",   "oper_not",     0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"&",   "oper_addr",    0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"++",  "oper_inc",     0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"--",  "oper_dec",     0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"++",  "oper_inc",     1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"--",  "oper_dec",     1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  {"*",   "oper_derefer", 0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1, -1},
		  // C/C++ binary operators
		  {"+",   "__add__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_add)},
		  {"-",   "__sub__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_subtract)},
		  {"*",   "__mul__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_multiply)},
		  {"/",   "__div__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_divide)},
		  {"%",   "__mod__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_remainder)},
		  {"^",   "__xor__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_xor)},
		  {"&",   "__and__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_and)},
		  {"|",   "__or__",       1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_or)},
		  {"<<",  "__lshift__",   1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_lshift)},
		  {">>",  "__rshift__",   1, PYMD_PYTHON_INTERNAL_SPECIAL_BINARY,  0, OFFSET(PyNumberMethods, nb_rshift)},
		  {"=",   "oper_assign",  1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"+=",  "__iadd__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_add)},
		  {"-=",  "__isub__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_subtract)},
		  {"*=",  "__imul__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_multiply)},
		  {"/=",  "__idiv__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_divide)},
		  {"%=",  "__imod__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_remainder)},
		  {"^=",  "__ixor__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_xor)},
		  {"&=",  "__iand__",     1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_and)},
		  {"|=",  "__ior__",      1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_or)},
		  {"<<=", "__ilshift__",  1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_lshift)},
		  {">>=", "__irshift__",  1, PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY, 0, OFFSET(PyNumberMethods, nb_inplace_rshift)},
		  {"==",  "oper_eq",      1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"!=",  "oper_noteq",   1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {">",   "oper_greater", 1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"<",   "oper_less",    1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {">=",  "oper_ge",      1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"<=",  "oper_le",      1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"&&",  "oper_land",    1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  {"||",  "oper_lor",     1, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,   -1,-1},
		  // intarg
		  {"[]",  "oper_getitem", 1, PYMD_PYTHON_INTERNAL_SPECIAL_INTARG,  1, OFFSET(PySequenceMethods, sq_item)},
		  // terminator
		  {NULL,  NULL,           0, PYMD_PYTHON_INTERNAL_SPECIAL_NONE,    0, 0}
	      };

	char *c_op=name+8;
	int i_operators=0;
	while(operators[i_operators].c_name)
	{
	    if(!strcmp(c_op, operators[i_operators].c_name) && n_params==operators[i_operators].n_c_params+1/*artificial this*/)
	    {
		// operator matches in name and in proto
		switch(operators[i_operators].i_struct)
		{
		case 0: // to.tp_as_number
		    if(!to.tp_as_number) to.tp_as_number=(PyNumberMethods*)calloc(1, sizeof(PyNumberMethods));
		    break;
		case 1: // to.tp_as_sequence
		    if(!to.tp_as_sequence) to.tp_as_sequence=(PySequenceMethods*)calloc(1, sizeof(PySequenceMethods));
		    break;
		case -1: // do nothing
		    break;
		default:
		    fprintf(stderr, "internal: TODO: unknown i_struct=%d", operators[i_operators].i_struct);
		}

		python_internal_offset=operators[i_operators].offset;
		python_internal_type=operators[i_operators].type;
		python_internal_i_struct=operators[i_operators].i_struct;
		python_name=const_cast<char*>(operators[i_operators].python_name);
		break;
	    }
	    i_operators++;
	}
	if(!operators[i_operators].c_name)
	{
	    // create unknown operator name
	    fprintf(stderr, "internal: TODO: unknown operator \"%s\"\n", c_op);
	    python_name=const_cast<char*>("operator_unknown");
	}
    }
    else
    {
	python_name=name;

	// attribute private
	if(attr.priv) python_name=rename_to_private(python_name);
    }

    // check if new method (polymorphism)
    int i_method;
    pymd_method_closure *closure;
    if((i_method=search_method(python_name, n_tp_methods))<0)
    {
	// new one (and new empty closure)
	i_method=inc_methods(n_tp_methods, max_tp_methods, n_method_closure, max_method_closure);
	PyMethodDef &methods=to.tp_methods[i_method];
	methods.ml_name=python_name;
	methods.ml_doc=gendata_get_string(position, doc, GENDATA_DOC);
	methods.ml_flags=METH_VARARGS;
	if(attr.nowrap)
	    methods.ml_meth=(PyCFunction)func;
	else
	    methods.ml_meth=pyfunc[i_method]; // it's important that tp_methods[n].ml_meth calls pyfunc[n]

	// closure
	closure=&method_closure[i_method];

	// ctor (only for methods)
	int pos_ctor;
	if(gendata_has_metatype(*position, GENDATA_IS_CTOR, &pos_ctor) && position[pos_ctor]) i_ctor=i_method;

	// special/internal methods
	if(python_internal_type!=PYMD_PYTHON_INTERNAL_SPECIAL_NONE)
	{
	    switch(python_internal_i_struct)
	    {
	    case 0: // to.tp_as_number
		*(unaryfunc*)((char*)to.tp_as_number+python_internal_offset)=(unaryfunc)pyfunc[i_method];
		break;
	    case 1: // to.tp_as_sequence
		*(unaryfunc*)((char*)to.tp_as_sequence+python_internal_offset)=(unaryfunc)pyfunc[i_method];
		break;
	    }
	}

	// specific special/internal methods (not operators)
	if(!strcmp(python_name, "__repr__") && n_params==1/*this*/)
	{
	    to.tp_repr=(reprfunc)methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_UNARY;
	}
	else if(!strcmp(python_name, "__cmp__") && n_params==2/*this*/)
	{
	    to.tp_compare=(cmpfunc)methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_NONE; // but should return an integer TODO
	}
	else if(!strcmp(python_name, "__richcmp__") && n_params==3/*this*/)
	{
	    to.tp_richcompare=(richcmpfunc)methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_NONE; // prototype not implemented TODO
	}
	else if(!strcmp(python_name, "__getitem__") && n_params==2/*this*/)
	{
	    if(!to.tp_as_sequence) to.tp_as_sequence=(PySequenceMethods*)calloc(1, sizeof(PySequenceMethods));
	    to.tp_as_sequence->sq_item=(typeof(to.tp_as_sequence->sq_item))methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_INTARG;
	}
	else if(!strcmp(python_name, "__setitem__") && n_params==3/*this*/)
	{
	    if(!to.tp_as_sequence) to.tp_as_sequence=(PySequenceMethods*)calloc(1, sizeof(PySequenceMethods));
	    to.tp_as_sequence->sq_ass_item=(typeof(to.tp_as_sequence->sq_ass_item))methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_NONE; // proto not implemented TODO
	}
	else if(!strcmp(python_name, "__len__") && n_params==1/*this*/)
	{
	    if(!to.tp_as_sequence) to.tp_as_sequence=(PySequenceMethods*)calloc(1, sizeof(PySequenceMethods));
	    to.tp_as_sequence->sq_length=(typeof(to.tp_as_sequence->sq_length))methods.ml_meth;
	    python_internal_type=PYMD_PYTHON_INTERNAL_SPECIAL_NONE; // but should return an integer TODO
	}

	closure->internal_type=python_internal_type;
	closure->internal_i_struct=python_internal_i_struct;
	closure->internal_offset=python_internal_offset;
    }
    else
	closure=&method_closure[i_method];

    // add method closure info
    int i_poly=closure->n_poly++;
    closure->poly=(struct pymd_method_closure::_poly_*)realloc(closure->poly, closure->n_poly*sizeof(struct pymd_method_closure::_poly_));
    closure->poly[i_poly].data=position;
    closure->poly[i_poly].func=func;

    // VTable
    int pos_vtable;
    if(gendata_has_metatype(*position, GENDATA_VTABLE, &pos_vtable))
	closure->poly[i_poly].vtable_pos=position[pos_vtable];
    else
	closure->poly[i_poly].vtable_pos=-1;

    // attributes
    closure->poly[i_poly].attr=attr;

    return 1;
}

bool python_type_metadata::check_attributes(int verbo)
{
    for(int i=0; get_method(i)->ml_name; i++)
    {
	for(int j=0; j<method_closure[i].n_poly; j++)
	{
	    pymd_attr_method a=method_closure[i].poly[j].attr;

	    // check special functions prototypes (non-static, no params)
	    if(a.prepickle || a.postunpickle || a.event_queue)
	    {
		int *position=method_closure[i].poly[j].data;
		int n_params=gendata_get_data(position, GENDATA_GENDATA_VAR);
		int *pos_params=position+gendata_get_position(position, GENDATA_GENDATA_VAR)+1;
		if(n_params!=1 || !gendata_get_data(pos_params, GENDATA_ARTIFICIAL))
		{
		    a.prepickle=a.postunpickle=a.event_queue=0;
		    if(verbo)
			fprintf(stderr, "internal: special method with incorrect proto: %s\n",
				gendata_get_string(position, doc, GENDATA_NAME));
		}

		// event_queue, in addition, must return an integer and
		// can only be used in reference counted objects
		if(a.event_queue)
		{
		    if(refcnt_location==GENDATA_INVALID_VALUE)
		    {
			a.event_queue=0;
			fprintf(stderr, "internal: cannot register event_queue methods of non-refcnt objects\n");
		    }
		    else if(gendata_resolve_type(position)!=GENDATA_INT)
		    {
			a.event_queue=0;
			if(verbo)
			    fprintf(stderr, "internal: event_queue method prototype is incorrect: %s\n",
				    gendata_get_string(position, doc, GENDATA_NAME));
		    }
		}
	    }
	}
    }
    return true;
}

pymd_attr_member python_type_metadata::parse_attr_member_string(char *attr_str)
{
    pymd_attr_member attr;
    memset(&attr, 0, sizeof(pymd_attr_member));

    if(attr_str)
    {
	// read only
	attr.read_only=parse_attr_bool(attr_str, ATTR_READ_ONLY) |
	    parse_attr_bool(attr_str, ATTR_READ_ONLY_ALIAS1);           // read_only
	attr.nopickle=parse_attr_bool(attr_str, ATTR_NOPICKLE);         // nopickle
	attr.priv=parse_attr_bool(attr_str, ATTR_PRIVATE);              // private
    }
    return attr;
}

pymd_attr_method python_type_metadata::parse_attr_method_string(char *attr_str)
{
    pymd_attr_method attr;
    memset(&attr, 0, sizeof(pymd_attr_method));

    if(attr_str)
    {
	attr.prepickle=parse_attr_bool(attr_str, ATTR_PREPICKLE);       // prepickle
	attr.postunpickle=parse_attr_bool(attr_str, ATTR_POSTUNPICKLE); // postunpickle
	attr.event_queue=parse_attr_bool(attr_str, ATTR_EVENT_QUEUE);   // event_queue
	attr.priv=parse_attr_bool(attr_str, ATTR_PRIVATE);              // private
	attr.nowrap=parse_attr_bool(attr_str, ATTR_NOWRAP);             // nowrap
    }
    return attr;
}

bool python_type_metadata::parse_attr_bool(char *attr_str, const char *attr_name)
{
    char *pos, *orig=attr_str;
    int n_attr_name=strlen(attr_name);
    while((pos=string::search(attr_str, const_cast<char *>(attr_name), const_cast<char*>("()"))))
    {
	if((pos==orig || (!isalnum(pos[-1]) && pos[-1]!='_' && pos[-1]!='~')) &&
	   (!pos[n_attr_name] || (!isalnum(pos[n_attr_name]) && pos[n_attr_name]!='_')))
	    return true;
	attr_str=pos+n_attr_name;
    }
    return false;
}

PyObject *python_type_metadata::parse_attr_from_metadata(int *data)
{
    PyObject *list=PyList_New(0);
    char *str;
    if((str=gendata_get_string(data, doc, GENDATA_ATTR)))
    {
	char *orig;
	while(*(orig=str))
	{
	    // search an attr
	    while(isalnum(*str) || *str=='_' || *str=='~') str++;
	    if(*str=='(')
	    {
		char *tmp=string::search(str+1, const_cast<char*>(")"), const_cast<char*>("\"\""));
		if(tmp) str=tmp+1;
	    }
	    char ctmp=*str;
	    *str=0;

	    // add this attribute to list
	    PyList_Append(list, PyString_FromString(orig));

	    if(ctmp)
	    {
		*str=ctmp;
		str++;
	    }
	    else break;
	}
    }
    return list;
}

void python_type_metadata::centinel_method(int &n_tp_methods, int &max_tp_methods)
{
    int i=util::inc_vector(to.tp_methods, n_tp_methods, max_tp_methods, INC_TP_METHODS);
    memset(&to.tp_methods[i], 0, sizeof(PyMethodDef));
}

int python_type_metadata::search_method(char *name, int n_method)
{
    for(int i=0; i<n_method; i++) if(to.tp_methods[i].ml_name==name) return i;
    return -1;
}

int python_type_metadata::search_method(const char *name)
{
    for(int i=0; to.tp_methods[i].ml_name; i++) if(!strcmp(to.tp_methods[i].ml_name, name)) return i;
    return -1;
}

void *python_type_metadata::search_method_entry_point(const char *name)
{
    int i_meth;
    if((i_meth=search_method(name))>=0)
    {
	pymd_method_closure &cl=method_closure[i_meth+method_closure_first];
	for(int i=0; i<cl.n_poly; i++)
	{
	    int *position=cl.poly[i].data;
	    int n_params=gendata_get_data(position, GENDATA_GENDATA_VAR);
	    int *pos_params=position+gendata_get_position(position, GENDATA_GENDATA_VAR)+1;
	    if(n_params!=1 || !gendata_get_data(pos_params, GENDATA_ARTIFICIAL)) continue;
	    return cl.poly[i].func;
	}
    }
    return NULL;
}

bool python_type_metadata::check_type_inheritance(python_type_metadata *given_type)
{
    // itself
    if(given_type==this) return true;

    // recursion
    if(to.tp_base && to.tp_base->tp_base)
	return ((python_type_metadata*)to.tp_base)->check_type_inheritance(given_type);

    return false;
}

char *python_type_metadata::create_name(char *name, char *namespc, char *module_name)
{
    char *new_name;
    if(!namespc)
    {
	new_name=(char*)malloc(strlen(name)+1+strlen(module_name)+1);
	sprintf(new_name, "%s.%s", module_name, name);
    }
    else
    {
	new_name=(char*)malloc(strlen(name)+1+strlen(namespc)+1+strlen(module_name)+1);
	sprintf(new_name, "%s.%s.%s", module_name, namespc, name);
    }
    return new_name;
}

char *python_type_metadata::create_private_name(char *name, char *module_name, int n_under)
{
    char *new_name=(char*)malloc(strlen(name)+1+n_under+strlen(module_name)+1);
    char *p=new_name;
    p+=sprintf(p, "%s.", module_name);
    for(int i=0; i<n_under; i++) *p++='_';
    sprintf(p, "%s", name);
    return new_name;
}

char *python_type_metadata::rename_to_private(char *name)
{
    int i=util::inc_vector(rename, n_rename, max_rename, INC_RENAME);
    rename[i]=(char*)malloc(strlen(name)+1+1);
    sprintf(rename[i], "_%s", name);
    return rename[i];
}

int python_type_metadata::inc_methods(int &n_tp_methods, int &max_tp_methods, int &n_method_closure, int &max_method_closure)
{
    int i_method=util::inc_vector(to.tp_methods, n_tp_methods, max_tp_methods, INC_TP_METHODS);
    int i_closure=util::inc_vector(method_closure, n_method_closure, max_method_closure, INC_TP_METHODS);
    memset(&method_closure[i_closure], 0, sizeof(pymd_method_closure));
    if(i_method!=i_closure) fprintf(stderr, "internal: impossible but checked\n");
    if(i_method>=PYMD_MAX_STATIC_METHODS) fprintf(stderr, "internal: maximum static number of methods reached, increase method count!\n");
    return i_method;
}

int python_type_metadata::add(PyObject *module)
{
    // save original name
    char *orig_name=const_cast<char*>(to.tp_name);

    // separate module and class name
    char *class_name;
    if(!(class_name=strrchr(orig_name, '.'))) ERROR_R0("Format module.name not detected");
    *class_name=0; class_name++;

    // module name
    char *namespc=python_container::split_namespace(orig_name);
    if(strcmp(PyModule_GetName(module), orig_name))
	ERROR_R0("Module name(%s) != class context(%s.%s)", PyModule_GetName(module), orig_name, namespc);

    // get module
    PyObject *module_ctx=python_container::route(module, namespc, NULL, 1);

    // container
    PyObject *container;
    if(!(container=python_container::new_container(&to))) ERROR_R0("Cannot create container");
    if(PyObject_SetAttrString(module_ctx, class_name, container)) ERROR_R0("PyObject_SetAttrString");
    Py_DECREF(container);

    // type class (with unique name)
    int n_under=1;
    while(1)
    {
	to.tp_name=create_private_name(class_name, PyModule_GetName(module), n_under);
	if(PyObject_HasAttrString(module, strchr(to.tp_name, '.')+1))
	{
	    n_under++;
	    free((void*)to.tp_name);
	    continue;
	}

	if(PyObject_SetAttrString(module, strchr(to.tp_name, '.')+1, (PyObject*)(void*)&to)) ERROR_R0("PyObject_SetAttrString");
	break;
    }
    free(orig_name);

    return 1;
}

int python_type_metadata::ready(int only_resolve_inheritance)
{
    // resolve inheritance issues (multiple inheritance not allowed for python extending system)
    int n_meta=gendata_get_data(data, GENDATA_GENDATA_VAR);
    int *position=data+gendata_get_position(data, GENDATA_GENDATA_VAR)+1;
    for(int i=0; i<n_meta; i++)
    {
	switch(*position)
	{
	case GENDATA_INHERITANCE:
	    {
		if(gendata_get_data(position, GENDATA_LOCATION)!=0)
		{
		    fprintf(stderr, "Inheritance is not at class start position\n");
		    break;
		}
		int *base;
		if(!(base=gendata_resolve_pos_type(position)))
		{
		    fprintf(stderr, "internal: Inheritance type not defined\n");
		    break;
		}
		python_type_metadata *type;
		if(!(type=(python_type_metadata *)gendata_get_data(base, GENDATA_GENDATA_RESERVE)))
		{
		    fprintf(stderr, "internal: No python type pointer in metadata structure\n");
		    break;
		}
		// let python know the base type
		to.tp_base=(PyTypeObject*)type;

		// recursively resolve inheritance hierarchy
		type->ready(1/*only inheritance*/);
	    }
	    break;
	default:
	    break;
	}
	position=gendata_traverse(position);
    }

    if(!only_resolve_inheritance)
    {
	// get the type ready
	Py_INCREF((PyObject*)&to);
	if(PyType_Ready(&to)<0) ERROR_R0("PyType_Ready: class %s", to.tp_name);
    }
    return 1;
}

int python_type_metadata::get_n_getsetters()
{
    if(!to.tp_getset) return 0;

    int i=0;
    while(to.tp_getset[i].name) i++;
    return i;
}

int python_type_metadata::search_getsetter(char *getsetter_name)
{
    if(!to.tp_getset) return -1;
    for(int i=0; to.tp_getset[i].name; i++) if(!strcmp(to.tp_getset[i].name, getsetter_name)) return i;
    return -1;
}

void python_type_metadata::update_refcnt_from_bases()
{
    // use reference count of bases
    PyTypeObject *type=to.tp_base;
    while(type && type->tp_base/*object is always the top base*/)
    {
	int location;
	if((location=((python_type_metadata*)type)->refcnt_location)!=GENDATA_INVALID_VALUE)
	{
	    if(refcnt_location!=GENDATA_INVALID_VALUE && refcnt_location!=location)
		fprintf(stderr, "Multiple reference counters for object\n");
	    refcnt_location=location; // TODO: only works if inheritances are always at location 0
	}
	type=type->tp_base;
    }
}

void python_type_metadata::update_methods()
{
    // check
    if(!to.tp_base)
    {
	fprintf(stderr, "internal: class w/o base after PyType_Ready\n");
	return;
    }

    // if type has python_type_metadata
    if(to.tp_base && to.tp_base->tp_base)
    {
	// do recursion first
	python_type_metadata *base=(python_type_metadata*)to.tp_base;
	base->update_methods();

	if(!method_closure_first)
	{
	    // do methods stuff
	    int n_method=0, n_method_base=0;
	    while(to.tp_methods[n_method].ml_name) n_method++;
	    while(base->to.tp_methods[n_method_base].ml_name) n_method_base++;
	    n_method_base+=base->method_closure_first;

	    // check maximum allowable static methods
	    if(n_method+n_method_base>=PYMD_MAX_STATIC_METHODS)
		fprintf(stderr, "internal: maximum static number of methods reached, increase method count!\n");

	    // resize and move method closure
	    method_closure=(pymd_method_closure*)realloc(method_closure, (n_method+n_method_base)*sizeof(pymd_method_closure));
	    memmove(&method_closure[n_method_base], method_closure, n_method*sizeof(pymd_method_closure));
	    memset(method_closure, 0, n_method_base*sizeof(pymd_method_closure));

	    // repoint method callbacks
	    for(int i=0; i<n_method; i++)
	    {
		if(i+n_method_base>=PYMD_MAX_STATIC_METHODS) to.tp_methods[i].ml_name=NULL;

		// repoint normal methods
		if(to.tp_methods[i].ml_meth==pyfunc[i])
		    to.tp_methods[i].ml_meth=pyfunc[i+n_method_base];

		// repoint special calls
		if(method_closure[i+n_method_base].internal_type!=PYMD_PYTHON_INTERNAL_SPECIAL_NONE)
		{
		    void **addr=NULL;
		    switch(method_closure[i+n_method_base].internal_i_struct)
		    {
		    case 0: // to.tp_as_number
			addr=(void**)((char*)to.tp_as_number+method_closure[i+n_method_base].internal_offset); break;
		    case 1: // to.tp_as_sequence
			addr=(void**)((char*)to.tp_as_sequence+method_closure[i+n_method_base].internal_offset); break;
		    }

		    if(*addr==pyfunc[i])
		    {
			if(i+n_method_base<PYMD_MAX_STATIC_METHODS)
			    *addr=(void*)pyfunc[i+n_method_base];
			else
			    *addr=NULL;
			break;
		    }
		    else fprintf(stderr, "internal: this was marked as special...\n");
		}
	    }

	    // update ctor
	    if(i_ctor>=0) i_ctor+=n_method_base;

	    // this is to know where is the first closure
	    method_closure_first=n_method_base;
	}
    }

    // check attributes
    if(!check_attributes()) fprintf(stderr, "internal: check attributes\n");
}

int python_type_metadata::pyinit(PyObject *self, PyObject *args, PyObject *kwds)
{
    return 0;
}

PyObject *python_type_metadata::pynew(PyTypeObject *type_, PyObject *args, PyObject *kwds)
{
    python_type_metadata *type=(python_type_metadata*)type_;

    // create real object / alloc + ctor
    int size=gendata_get_data(type->data, GENDATA_SIZE);
    if(size==GENDATA_INVALID_VALUE)
    {
	PyErr_SetString(PyExc_TypeError, "Invalid size of object");
	return NULL;
    }
    else if(!size) size++;
    void *obj=calloc(1, size);

    // create wrapper python object
    python_metadata *python_obj;
    if(!(python_obj=python_metadata::new_python_metadata(obj, 0/*indirection*/, NULL/*obj_container*/, type, args))) free(obj);
    return (PyObject*)python_obj;
}

void python_type_metadata::pydealloc(PyObject *self)
{
    python_metadata::delete_python_metadata((python_metadata *)self);
}

int python_type_metadata::pycompare(PyObject *a, PyObject *b)
{
    if(a->ob_type == b->ob_type)
    {
	if(((python_metadata *)a)->get_obj() < ((python_metadata *)b)->get_obj()) return -1;
	else if(((python_metadata *)a)->get_obj() > ((python_metadata *)b)->get_obj()) return 1;
	else return 0;
    }
    else if(a->ob_type < b->ob_type) return -1;
    else return 1;
}

void python_type_metadata::cdealloc(void *obj)
{
    if(obj)
    {
	if(dtor) ((void (*)(void*))dtor)(obj); // why dwarf says dtor has a second artificial param?
	// TODO: what does "__in_chrg" means?
	else
	{
	    // try to execute dtors of bases
	    python_type_metadata *base=this;
	    while(base->to.tp_base && base->to.tp_base->tp_base)
	    {
		base=(python_type_metadata *)base->to.tp_base;
		if(base->dtor)
		{
		    ((void (*)(void*))base->dtor)(obj); // dtor executed
		    break; // when first one executed, the rest are executed in cascade
		}
	    }
	}

	// the dtor shoud not delete/dealloc object, so do it manually
	// NOTE: dtor mangled names ending in D0Ev are deleting/deallocing objects themselves,
	//       but metadata system tries to get D1Ev ending ones...
	// TODO: need further investigations, see notes in dwarf.cc method:
	//       int dwarf::find_function_symbol(string_index *symtab, dwarf_base *b)
	free(obj);
    }
}

int python_type_metadata::pygetset_common(PyObject *self, void *closure,
					  void **type_pointer, int **data,
					  int **pos_type_metadata, int n_indirections)
{
    pymd_getset_closure *cl;
    int location;

    // static checks (not required when all is ok, but are good for debugging)
    if(!(cl=(pymd_getset_closure *)closure))
    {
	fprintf(stderr, "no closure in get_member\n");
	return 0;
    }
    if(!(*pos_type_metadata=gendata_resolve_pos_type(cl->data, n_indirections)))
    {
	fprintf(stderr, "internal: unset or wrong type reference in metadata: n_indir=%d\n", n_indirections);
	gendata_dump(stderr, cl->data, 1, pymdg.get_meta(0)->doc);
	return 0;
    }
    if((location=gendata_get_data(cl->data, GENDATA_LOCATION))<0)
    {
	fprintf(stderr, "internal: location\n");
	return 0;
    }

    // obtain type_pointer
    python_metadata *pyobj=(python_metadata*)self;
    char *obj;
    if(!(obj=(char*)pyobj->get_obj())) return 0;
    *type_pointer=obj+location;

    if(data) *data=cl->data;
    return 1;
}

PyObject *python_type_metadata::pyget_member(PyObject *self, void *closure)
{
    void *type_pointer;
    int *pos_type_metadata, type_metadata=-1;
    md_c_to_python_func func;
    if(!pygetset_common(self, closure, &type_pointer, NULL, &pos_type_metadata) ||
       (type_metadata=*pos_type_metadata)<0 ||
       !(func=pymdg.get_func_c_to_python(type_metadata)))
    {
	if(((python_metadata*)self)->check_vanished()) return NULL;

	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check get function for type %s",
			       type_metadata>=0?gendata_type_strings[type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }
    return func(type_pointer, pos_type_metadata);
}

int python_type_metadata::pyset_member(PyObject *self, PyObject *value, void *closure)
{
    void *type_pointer;
    int *pos_type_metadata, type_metadata=-1;
    md_python_to_c_func func;
    if(!pygetset_common(self, closure, &type_pointer, NULL, &pos_type_metadata) ||
       (type_metadata=*pos_type_metadata)<0 ||
       !(func=pymdg.get_func_python_to_c(type_metadata)))
    {
	if(((python_metadata*)self)->check_vanished()) return -1;

	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check set function for type %s",
			       type_metadata>=0?gendata_type_strings[type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }

    // read only
    if(((pymd_getset_closure *)closure)->attr.read_only)
    {
	PyErr_SetString(PyExc_AttributeError, "read only attribute");
	return -1;
    }

    return func(value, type_pointer, pos_type_metadata);
}

PyObject *python_type_metadata::pyget_member_object(PyObject *self, void *closure)
{
    // very similar to pymetadata_global::c_to_python_class() but here container already known (self)

    void *type_pointer;
    int *pos_type_metadata, *data;
    if(!pygetset_common(self, closure, &type_pointer, &data, &pos_type_metadata))
    {
	PyErr_SetString(PyExc_TypeError, "internal: pygetset_common");
	return NULL;
    }

    python_type_metadata *type;
    if(!(type=(python_type_metadata *)gendata_get_data(pos_type_metadata, GENDATA_GENDATA_RESERVE)))
    {
	ERROR("internal: class doc->%d not initialized", gendata_get_data(pos_type_metadata, GENDATA_NAME));
	PyErr_SetString(PyExc_TypeError, "internal: class or structure not initialized");
	return NULL;
    }

    // create new (or new reference of) python_metadata object
    python_metadata *python_obj;
    if(!(python_obj=python_metadata::new_python_metadata(type_pointer, 0/*indirection*/, (python_metadata*)self, type, NULL/*ctor_args*/)))
	PyErr_SetString(PyExc_TypeError, "internal: cannot create new python metadata in member_obj");
    return (PyObject*)python_obj;
}

int python_type_metadata::pyset_member_object(PyObject *self, PyObject *value, void *closure)
{
    // very similar to pymetadata_global::python_to_c_class() but here container already known (self)

    void *type_pointer;
    int *pos_type_metadata, *data;
    if(!pygetset_common(self, closure, &type_pointer, &data, &pos_type_metadata))
    {
	PyErr_SetString(PyExc_TypeError, "internal: pygetset_common");
	return -1;
    }

    // read only
    if(((pymd_getset_closure *)closure)->attr.read_only)
    {
	PyErr_SetString(PyExc_AttributeError, "read only attribute");
	return -1;
    }

    // contained object type
    if(!(data=gendata_resolve_pos_type(data)))
    {
	PyErr_SetString(PyExc_TypeError, "internal: contained object metadata w/o type");
	return -1;
    }

    return pyset_member_object(self, value, type_pointer, data);
}

int python_type_metadata::pyset_member_object(PyObject *self, PyObject *value, void *type_pointer, int *data)
{
    python_type_metadata *type=(python_type_metadata *)gendata_get_data(data, GENDATA_GENDATA_RESERVE);

    // check types (must match)
    if(type!=(python_type_metadata*)value->ob_type)
    {
#if 0
	ERROR("%s != %s", type->get_name(), value->ob_type->tp_name);
	gendata_dump(stdout, ((pymd_getset_closure *)closure)->data, 1, type->doc);
#endif

	PyErr_SetString(PyExc_TypeError, "procedural: exact type required for contained object assignments");
	return -1;
    }

    // types of value and included object match, now check python to C++ interoperativity rules
    // this implies that value is from python_metadata class
    // this case is: to C1A2, C2A2 == P3, allowed from P1, P2 and P3
    python_metadata *pyvalue=(python_metadata *)value;
    int was_p3=0;
    if(pyvalue->get_obj_container())
    {
	if(pyvalue->get_indirection())
	{
	    pymdg.dump_object_list(pyvalue);
	    PyErr_SetString(PyExc_TypeError, "procedural: sorry, cannot assign a reference contained in other object (P4) to this one");
	    return -1;
	}
	was_p3++;
    }
    if(pyvalue->has_refcnt() && pyvalue->get_refcnt()!=1)
    {
	PyErr_SetString(PyExc_TypeError, "procedural: sorry, cannot assign a multiple reference counted object to this one");
	return -1;
    }
    void *dynamic_obj;
    if(!(dynamic_obj=pyvalue->get_obj()))
    {
	PyErr_SetString(PyExc_TypeError, "internal: python metadata w/o C++ obj");
	return -1;
    }

    // ok, proceed: P1, P2 or P3 -> C1A2, C2A2
    int c_size=pyvalue->get_c_size();

#if 0
    // DEBUG2
    ERROR("TODO1");
    pymdg.dump_object_list(pyvalue);
#endif

    // 1, check if there is a python reference of this contained object
    python_metadata *pyold;
    python_metadata *pyobj=(python_metadata *)self;
    int must_decrease_python_count=0, i_pyold;
    python_metadata *pyobj_buffer[MAX_PYMD_BUFFER];
    if((pyold=pymdg.search_object(type_pointer, type, &i_pyold)))
    {
	// there is already a P3 for this C++ object
	// check that it's P3 actually
	if(!pyold->get_obj_container() || pyold->get_indirection())
	{
	    PyErr_SetString(PyExc_TypeError, "internal: old python_metadata should be a P3");
	    return -1;
	}
	// check C++ referneces
	if(pyold->has_refcnt() && pyold->get_refcnt()!=1)
	{
	    PyErr_SetString(PyExc_TypeError, "internal: P3 python_metadata with reference count !=1");
	    return -1;
	}
	// check that pyold!=pyvalue
	if(pyold==pyvalue) return 0; // nothing to do, assigning same object

	// must convert this P3 to a P1 or P2
	int n=pymdg.n_contained_objects(i_pyold);

	// 1.1a, unregister object(s)
	if(n>=MAX_PYMD_BUFFER) ERROR_R0("internal: increase MAX_PYMD_BUFFER");
	if(!pymdg.remove_object(i_pyold, n+1, pyobj_buffer)) ERROR("internal: pyold not registered when trying to remove it");

	// 1.2, create space for C++ object
	void *old_obj=malloc(c_size?c_size:1);

	// 1.3, copy C++ obj contents
	memcpy(old_obj, type_pointer, c_size);

	// 1.4, convert P3 to P1 or P2 and increase C++ refcnt if required
	pyold->transmute_object_type(old_obj);
	must_decrease_python_count++;
	if(pyold->has_refcnt() && !pyold->get_refcnt()) pyold->c_incref();

	// 1.5, register obj and take care of previous contained objects
	if(pymdg.add_objects(pyobj_buffer, n+1)<0) ERROR("internal: adding pyold");
    }
    else
	// 1.1b, call contained obj destructor (no dealloc)
	if(type->dtor) ((void (*)(void*))type->dtor)(type_pointer);

    // 2, obtain info about pyvalue and unregister obj(s)
    int i_pyvalue, n;
    if((i_pyvalue=pymdg.search_object(pyvalue))<0)
    {
	pymdg.dump_object_list(pyvalue);
	ERROR_R0("internal: pyvalue %p not registered", pyvalue);
    }
    n=pymdg.n_contained_objects(i_pyvalue);
    if(!pymdg.remove_object(i_pyvalue, n+1, pyobj_buffer)) ERROR("internal: pyvalue not registered when trying to remove it");

    // 3, copy dynamic obj into contained one
    memcpy(type_pointer, dynamic_obj, c_size);

    // 4, free (no dtor) dynamic obj
    if(was_p3)
    {
	memset(dynamic_obj, 0, c_size);
	if(type->dtor) ((void (*)(void*))type->dtor)(dynamic_obj);
	pyvalue->get_obj_container()->python_decref();
    }
    else
	free(dynamic_obj);

    // 5, convert P1, P2 or P3 into P3 and reset reference count of new P3 if it has one
    if(type->refcnt_location!=GENDATA_INVALID_VALUE) *(int*)((char*)type_pointer+type->refcnt_location)=0;
    pyvalue->transmute_object_type(0/*indirection*/, (int)type_pointer-(int)pyobj->get_obj(), pyobj);
    pyobj->python_incref();

    // 6, register new object
    if(pymdg.add_objects(pyobj_buffer, n+1)<0) ERROR("internal: adding pyvalue");

    // 7, decrease count if there was a P3 for contained obj
    if(must_decrease_python_count) pyobj->python_decref();

#if 0
    // DEBUG2
    ERROR("TODO2");
    pymdg.dump_object_list((python_metadata *)value);
#endif
    return 0;
}

PyObject *python_type_metadata::pyget_member_std_vector_impl(PyObject *self, void *closure)
{
    void *type_pointer;
    int *data, *pos_type_metadata, type_metadata=-1;
    md_c_to_python_func func;
    if(!pygetset_common(self, closure, &type_pointer, &data, &pos_type_metadata, 2) ||
       (type_metadata=*pos_type_metadata)<0 ||
       !(func=pymdg.get_func_c_to_python(type_metadata)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check get function for type %s",
			       type_metadata>=0?gendata_type_strings[type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }

    PyObject *list=PyList_New(0);
#if 0
    // get n_vector and vector item size
    int n, size;
    int location;
    location=gendata_get_data(data, GENDATA_LOCATION, 1); // relative location of n_vector
    n=*(int*)((char*)((python_metadata*)self)->get_obj()+location);
    size=gendata_get_data(pos_type_metadata, GENDATA_SIZE);

    // pointer to first item
    type_pointer=*(void**)type_pointer;

    // generate list
    PyObject *list=PyList_New(n);
    for(int i=0; i<n; i++)
    {
	PyObject *obj=func(type_pointer, pos_type_metadata);
	if(!obj)
	{
	    // delete list
	    Py_DECREF(list);

	    // make an elaborated error string
	    PyObject *exception;
	    pymdg.clear_error_string();
	    pymdg.add_error_string("element at position %d: %s", i, pymdg.python_error_string(&exception));
	    PyErr_SetString(exception, pymdg.get_error_string());
	    return NULL;
	}
	PyList_SetItem(list, i, obj);
	type_pointer=(char*)type_pointer+size;
    }
#endif
    return list;
}

int python_type_metadata::pyset_member_std_vector_impl(PyObject *self, PyObject *value, void *closure)
{
    ERROR("TODO");
    return 0;
}

PyObject *python_type_metadata::pyget_member_vector_n_max(PyObject *self, void *closure)
{
#if 1

    // using python_vector
    int *data=((pymd_getset_closure *)closure)->data;
    int pos_vector=gendata_get_data(data, GENDATA_LOCATION, 0); // relative location of vector
    void *vector=(char*)((python_metadata*)self)->get_obj()+pos_vector;
    python_metadata *pyv=python_metadata::new_python_metadata(vector, 1, (python_metadata*)self, pymdg.get_type_python_vector(), NULL);
    return (PyObject*)pyv;

#else

    void *type_pointer;
    int *data, *pos_type_metadata, type_metadata=-1;
    md_c_to_python_func func;
    if(!pygetset_common(self, closure, &type_pointer, &data, &pos_type_metadata, 2) ||
       (type_metadata=*pos_type_metadata)<0 ||
       !(func=pymdg.get_func_c_to_python(type_metadata)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check get function for type %s",
			       type_metadata>=0?gendata_type_strings[type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }

    // get n_vector and vector item size
    int n, size;
    int location;
    location=gendata_get_data(data, GENDATA_LOCATION, 1); // relative location of n_vector
    n=*(int*)((char*)((python_metadata*)self)->get_obj()+location);
    size=gendata_get_data(pos_type_metadata, GENDATA_SIZE);

    // pointer to first item
    type_pointer=*(void**)type_pointer;

    // generate list
    PyObject *list=PyList_New(n);
    for(int i=0; i<n; i++)
    {
	PyObject *obj=func(type_pointer, pos_type_metadata);
	if(!obj)
	{
	    // delete list
	    Py_DECREF(list);

	    // make an elaborated error string
	    PyObject *exception;
	    pymdg.clear_error_string();
	    pymdg.add_error_string("element at position %d: %s", i, pymdg.python_error_string(&exception));
	    PyErr_SetString(exception, pymdg.get_error_string());
	    return NULL;
	}
	PyList_SetItem(list, i, obj);
	type_pointer=(char*)type_pointer+size;
    }
    return list;

#endif
}

int python_type_metadata::pyset_member_vector_n_max(PyObject *self, PyObject *value, void *closure)
{
    // checks of value
    if(!value)
    {
	PyErr_SetString(PyExc_TypeError, "an object is required");
	return -1;
    }

    // check if a python_vector
    if((python_type_metadata*)value->ob_type==pymdg.get_type_python_vector())
    {
	PyObject *value_list=python_vector::get_list(value);

	// transmute value from P2 to P4
	python_metadata *pyvalue=(python_metadata*)value;
	if(!pyvalue->get_obj_container())
	{
	    //pymdg.dump_object_list(pyvalue);
	    pymdg.remove_object(pyvalue);

	    delete (python_vector*)pyvalue->get_obj();
	    int *data=((pymd_getset_closure *)closure)->data;
	    int pos_vector=gendata_get_data(data, GENDATA_LOCATION, 0); // relative location of vector
	    python_metadata *pyobj=(python_metadata*)self;
	    pyvalue->transmute_object_type(1/*indirection*/, pos_vector, pyobj/*container*/);
	    pyobj->python_incref();

	    pymdg.add_object(pyvalue);
	    //pymdg.dump_object_list(pyvalue);
	}

	// now set list
	int ret=pyset_member_vector_n_max(self, value_list, closure);
	Py_DECREF(value_list);
	return ret;
    }

    // checks of value
    if(!PyList_Check(value))
    {
	PyErr_SetString(PyExc_TypeError, "a list is required");
	return -1;
    }

    void **type_pointer;
    int *data, *pos_type_metadata, type_metadata=-1, size;
    md_python_to_c_func func_python_to_c;
    md_free_c_func func_free_c;
    if(!pygetset_common(self, closure, (void**)(void*)&type_pointer, &data, &pos_type_metadata, 2) ||
       (type_metadata=*pos_type_metadata)<0 ||
       !(func_python_to_c=pymdg.get_func_python_to_c(type_metadata)) ||
       (size=gendata_get_data(pos_type_metadata, GENDATA_SIZE))<=0)
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check set function for type %s",
			       type_metadata>=0?gendata_type_strings[type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }
    func_free_c=pymdg.get_func_free_c(type_metadata);

    // read only
    if(((pymd_getset_closure *)closure)->attr.read_only)
    {
	PyErr_SetString(PyExc_AttributeError, "read only attribute");
	return -1;
    }

    // get locations of n_vector and max_vector
    int *n, *max, location;
    location=gendata_get_data(data, GENDATA_LOCATION, 1);
    n=(int*)((char*)((python_metadata*)self)->get_obj()+location);
    location=gendata_get_data(data, GENDATA_LOCATION, 2);
    max=(int*)((char*)((python_metadata*)self)->get_obj()+location);

    // get old values
    void *old_vector=*type_pointer;
    int n_old_vector=*n;
    int max_old_vector=*max;

    // create a new C vector
    int n_new_vector=PyList_Size(value);
    void *new_vector=NULL;
    if(n_new_vector) new_vector=calloc(n_new_vector, size); // zeroed

    // store the values before setting the items
    *type_pointer=new_vector;
    *n=*max=n_new_vector;

    // set the items
    for(int i=0; i<n_new_vector; i++)
    {
	void *pos_vector=(char*)new_vector+i*size;
	PyObject *obj=PyList_GetItem(value, i);
	if(!obj || func_python_to_c(obj, pos_vector, pos_type_metadata)<0)
	{
	    // free vector and elements assigned so far
	    if(func_free_c)
		for(int j=0; j<i; j++)
		    func_free_c((char*)new_vector+i*size, pos_type_metadata);
	    free(new_vector);

	    // set old values
	    *type_pointer=old_vector;
	    *n=n_old_vector;
	    *max=max_old_vector;

	    // make an elaborated error string
	    PyObject *exception;
	    pymdg.clear_error_string();
	    pymdg.add_error_string("element at position %d: %s", i, pymdg.python_error_string(&exception));
	    PyErr_SetString(exception, pymdg.get_error_string());
	    return -1;
	}
    }

    // free old vector
    if(old_vector)
    {
	if(func_free_c)
	    for(int i=0; i<n_old_vector; i++)
		func_free_c((char*)(old_vector)+i*size, pos_type_metadata);
	free(old_vector);
    }
    return 0;
}

PyObject *python_type_metadata::pyget_constant(PyObject *self, void *closure)
{
    ERROR("!!");

    pymd_getset_closure *cl;
    if(!(cl=(pymd_getset_closure *)closure))
    {
	PyErr_SetString(PyExc_TypeError, "internal: no closure");
	return NULL;
    }

    return PyInt_FromLong(cl->value);
}

int python_type_metadata::pyset_constant(PyObject *self, PyObject *value, void *closure)
{
    PyErr_SetString(PyExc_TypeError, "cannot modify constant value");
    return -1;
}

bool python_type_metadata::call_prepickle_methods(PyObject *self)
{
    // do inheritance recursion, but omitting last "object" python base class
    if(to.tp_base && to.tp_base->tp_base/*"object" base type has no inheritance*/)
	((python_type_metadata*)to.tp_base)->call_prepickle_methods(self);

    if(!has_methods() || !to.tp_base) return true;
    for(int i=0; get_method(i)->ml_name; i++)
    {
	for(int j=0; j<method_closure[i+method_closure_first].n_poly; j++)
	{
	    if(method_closure[i+method_closure_first].poly[j].attr.prepickle)
	    {
		// call this method
		PyObject *obj;
		if(!(obj=pyfunc_resolver(i+method_closure_first, self, NULL/*args*/)))
		    ERROR_R0("executing prepickle method");
		else Py_DECREF(obj);

		// only one prototype possible
		break;
	    }
	}
    }
    return true;
}

bool python_type_metadata::call_postunpickle_methods(PyObject *self)
{
    // do inheritance recursion, but omitting last "object" python base class
    if(to.tp_base && to.tp_base->tp_base/*"object" base type has no inheritance*/)
	((python_type_metadata*)to.tp_base)->call_postunpickle_methods(self);

    if(!has_methods() || !to.tp_base) return true;
    for(int i=0; get_method(i)->ml_name; i++)
    {
	for(int j=0; j<method_closure[i+method_closure_first].n_poly; j++)
	{
	    if(method_closure[i+method_closure_first].poly[j].attr.postunpickle)
	    {
		// call this method
		PyObject *obj;
		if(!(obj=pyfunc_resolver(i+method_closure_first, self, NULL/*args*/)))
		    ERROR_R0("executing postunpickle method");
		else Py_DECREF(obj);

		// only one prototype possible
		break;
	    }
	}
    }
    return true;
}

bool python_type_metadata::register_event_queue_methods(PyObject *self)
{
    // do inheritance recursion, but omitting last "object" python base class
    if(to.tp_base && to.tp_base->tp_base/*"object" base type has no inheritance*/)
	((python_type_metadata*)to.tp_base)->call_postunpickle_methods(self);

    if(!has_methods() || !to.tp_base) return true;
    python_metadata *obj=(python_metadata *)self;
    for(int i=0; get_method(i)->ml_name; i++)
    {
	pymd_method_closure &c=method_closure[i+method_closure_first];
	for(int j=0; j<c.n_poly; j++)
	{
	    if(c.poly[j].attr.event_queue &&
	       !pymdg.event_queue_add(obj, gendata_get_string(c.poly[j].data, doc, GENDATA_NAME)))
		ERROR_RX(false, "internal: registering event queue method");
	}
    }
    return true;    
}

PyObject *python_type_metadata::pygetmdattribute(PyObject *self, PyObject *args)
{
    PyObject *str=NULL;
    if(args && PyArg_ParseTuple(args, (char*)"|S", &str))
    {
	// class attributes
	python_type_metadata *type=(python_type_metadata *)self->ob_type;
	if(!str) return type->parse_attr_from_metadata(type->data);

	// member
	char *name=PyString_AsString(str);
	int i;
	if((i=type->search_getsetter(name))>=0)
	{
	    pymd_getset_closure *cl=(pymd_getset_closure *)type->to.tp_getset[i].closure;
	    if(cl && cl->data) return type->parse_attr_from_metadata(cl->data);
	    PyErr_SetString(PyExc_AttributeError, "internal: metadata error");
	    return NULL;
	}

	// search method
	if((i=type->search_method(name))>=0)
	{
	    pymd_method_closure *cl=&type->method_closure[i+type->method_closure_first];
	    if(cl && cl->poly && cl->poly[0].data)
		return type->parse_attr_from_metadata(cl->poly[0].data);
	    PyErr_SetString(PyExc_AttributeError, "internal: metadata error");
	    return NULL;
	}

	PyErr_SetString(PyExc_AttributeError, "Attribute not found");
    }
    return NULL;
}

PyObject *python_type_metadata::pyget_member_static(PyObject *self, void *closure)
{
    pymd_getset_closure *cl=(pymd_getset_closure *)closure;
    int *pos_type_metadata;
    void *type_pointer;
    if(!(pos_type_metadata=gendata_resolve_pos_type(cl->data)))
    {
	PyErr_SetString(PyExc_TypeError, "internal: static member w/o type");
	return NULL;
    }
    if(!(type_pointer=(void*)gendata_get_data(cl->data, GENDATA_GENDATA_RESERVE)))
    {
	PyErr_SetString(PyExc_TypeError, "internal: static member /o address");
	return NULL;
    }
    md_c_to_python_func func;
    if(!(func=pymdg.get_func_c_to_python(*pos_type_metadata)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check get function for type %s",
			       *pos_type_metadata>=0?gendata_type_strings[*pos_type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return NULL;
    }
    return func(type_pointer, pos_type_metadata);
}

int python_type_metadata::pyset_member_static(PyObject *self, PyObject *value, void *closure)
{
    pymd_getset_closure *cl=(pymd_getset_closure *)closure;
    int *pos_type_metadata;
    void *type_pointer;
    if(!(pos_type_metadata=gendata_resolve_pos_type(cl->data)))
    {
	PyErr_SetString(PyExc_TypeError, "internal: static member w/o type");
	return -1;
    }
    if(!(type_pointer=(void*)gendata_get_data(cl->data, GENDATA_GENDATA_RESERVE)))
    {
	PyErr_SetString(PyExc_TypeError, "internal: static member /o address");
	return -1;
    }
    md_python_to_c_func func;
    if(!(func=pymdg.get_func_python_to_c(*pos_type_metadata)))
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("internal: check get function for type %s",
			       *pos_type_metadata>=0?gendata_type_strings[*pos_type_metadata]:"unknown");
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return -1;
    }
    return func(value, type_pointer, pos_type_metadata);
}

PyObject *python_type_metadata::pygetstate(PyObject *self)
{
    if(!((python_type_metadata *)self->ob_type)->call_prepickle_methods(self))
    {
	// do NOT fail, only show an error message in stderr
	fprintf(stderr, "Warning: object (%p:%s), cannot call prepickle methods: %s\n",
		self,
		self->ob_type->tp_name,
		pymetadata_global::python_error_string());
	PyErr_Clear();
    }

    // do NOT fail
    PyObject *dict=PyDict_New();
    pygetstate_add_to_dict(self, self->ob_type, dict);

    // save references if any
    python_metadata *container, *pyobj=(python_metadata*)self;
    if((container=pyobj->get_obj_container()))
	PyDict_SetItemString(dict, "__obj_container__", (PyObject*)container);

    return dict;
}

PyObject *python_type_metadata::pygetstate_gc(PyObject *self)
{
    PyObject *dict=PyDict_New();
    pygetstate_add_to_dict(self, self->ob_type, dict, true/*gc*/);
    return dict;
}

void python_type_metadata::pygetstate_add_to_dict(PyObject *self, PyTypeObject *type, PyObject *dict, bool gc)
{
    PyObject *item;
    if(type->tp_getset)
    {
	for(int i=0; ; i++)
	{
	    PyGetSetDef &getset=type->tp_getset[i];
	    if(!getset.name) break;

	    // check if pickle required on member: NOPICKLE
	    pymd_getset_closure *cl=(pymd_getset_closure *)getset.closure;
	    if(cl->attr.nopickle && !gc) continue;

	    if((item=getset.get(self, getset.closure)))
	    {
		PyDict_SetItemString(dict, getset.name, item);
		Py_DECREF(item);
	    }
	    else
	    {
		// do NOT fail
		fprintf(stderr, "Warning: object (%p:%s), cannot get item (%s) state: %s\n",
			self,
			type->tp_name,
			getset.name,
			pymetadata_global::python_error_string());
		PyErr_Clear();
	    }
	}
    }
    if(type->tp_base && type->tp_base->tp_base/*object is always the top base*/)
	pygetstate_add_to_dict(self, type->tp_base, dict);
}

PyObject *python_type_metadata::pysetstate(PyObject *self, PyObject *args)
{
    PyObject *dict;
    if(args && PyArg_ParseTuple(args, (char*)"O", &dict) && PyDict_Check(dict))
    {
	// do NOT fail
	pysetstate_get_from_dict(self, self->ob_type, dict);

	if(!((python_type_metadata *)self->ob_type)->call_postunpickle_methods(self))
	{
	    // do NOT fail, only show an error message in stderr
	    fprintf(stderr, "Warning: object (%p:%s), cannot call postunpickle methods: %s\n",
		    self,
		    self->ob_type->tp_name,
		    pymetadata_global::python_error_string());
	    PyErr_Clear();
	}
	Py_RETURN_NONE;
    }
    if(!PyErr_Occurred()) PyErr_SetString(PyExc_TypeError, "A dictionary is required");
    return NULL;
}

void python_type_metadata::pysetstate_get_from_dict(PyObject *self, PyTypeObject *type, PyObject *dict)
{
    if(type->tp_getset)
    {
	for(int i=0; ; i++)
	{
	    PyGetSetDef &getset=type->tp_getset[i];
	    if(!getset.name) break;

	    // nopickle
	    pymd_getset_closure *cl=(pymd_getset_closure *)getset.closure;
	    if(cl->attr.nopickle) continue;

	    PyObject *item;
	    if((item=PyDict_GetItemString(dict, getset.name)))
	    {
		// make member writable
		int writable=cl->attr.read_only;
		cl->attr.read_only=0;

		if(getset.set(self, item, getset.closure)<0)
		{
		    // do NOT fail
		    fprintf(stderr, "Warning: object (%p:%s), cannot set item (%s) state: %s\n",
			    self,
			    type->tp_name,
			    getset.name,
			    pymetadata_global::python_error_string());
		    PyErr_Clear();
		}

		cl->attr.read_only=writable;
	    }
	}
    }
    if(type->tp_base && type->tp_base->tp_base/*object is always the top base*/)
	pysetstate_get_from_dict(self, type->tp_base, dict);
}

PyObject *python_type_metadata::pygetinitargs(PyObject *self)
{
    return PyTuple_New(0);
}

PyObject *python_type_metadata::pyreduce(PyObject *self)
{
    PyObject *tupla=PyTuple_New(3), *initargs=PyTuple_New(0);
    PyObject *dict=pygetstate(self), *init=(PyObject*)self->ob_type;
    Py_INCREF(init);
    PyTuple_SET_ITEM(tupla, 0, init);
    PyTuple_SET_ITEM(tupla, 1, initargs);
    PyTuple_SET_ITEM(tupla, 2, dict);
    return tupla;
}

PyObject *python_type_metadata::pyreduce_ex(PyObject *self, PyObject *args)
{
    return pyreduce(self);
}

PyObject *python_type_metadata::pydebug(PyObject *self)
{
    return pydebug_common(self, 0);
}

PyObject *python_type_metadata::pydebug_ex(PyObject *self)
{
    return pydebug_common(self, 1);
}

PyObject *python_type_metadata::pydebug_common(PyObject *self, int extra_info)
{
    if(!self)
    {
	fprintf(stderr, "internal: no self in member function\n");
	Py_RETURN_NONE;
    }
    python_metadata *obj=(python_metadata*)self;
    python_type_metadata *type=(python_type_metadata*)self->ob_type;
    if(!type)
    {
	fprintf(stderr, "internal: python object of unknown python type\n");
	Py_RETURN_NONE;
    }

    printf(RED"Python object:   "RESET"%p\n", obj);
    printf(RED"Python size:     "RESET"%d bytes\n", self->ob_type->tp_basicsize);
    printf(RED"Python RefCnt:   "RESET"%d (interpreter shows 1 phanton reference)\n", self->ob_refcnt);
    printf(RED"C++ object:      "RESET"%p\n", obj->get_obj());
    printf(RED"C++ size:        "RESET"%d bytes\n", gendata_get_data(type->data, GENDATA_SIZE));
    if(obj->has_refcnt())
	printf(RED"C++ RefCnt:      "RESET"%d\n", obj->get_refcnt());

    type->dump(extra_info);
    Py_RETURN_NONE;
}

PyObject *python_type_metadata::pydebug_global(PyObject *self)
{
    printf(GREEN"Global metadata:"RESET"\n");
    pymdg.dump_object_list((python_metadata*)self);
    Py_RETURN_NONE;
}

PyObject *python_type_metadata::pydebug_internal(PyObject *self)
{
    python_metadata *pyobj=(python_metadata*)self;
    PyObject *tuple=PyTuple_New(4);
    PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(pyobj->get_obj()?1:0));
    PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(pyobj->get_obj_container()?1:0));
    PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(pyobj->has_refcnt()?pyobj->get_refcnt():-1));
    PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong((int)pyobj->get_obj_type()));
    return tuple;
}

PyObject *python_type_metadata::pydebug_gc(PyObject *self)
{
    pymetadata_gc gc;
    gc.add((python_metadata *)self);
    gc.debug();
    Py_RETURN_NONE;
}

void python_type_metadata::dump(int extra_info, int level)
{
    int n_buffer=256;
    char buffer[n_buffer];

    printf(RED"%*sClass:           "RESET"%p %s\n", level*5, "", this, get_name());

    if(has_getsetters())
    {
	printf(RED"%*sGetsetters:"RESET"\n", level*5, "");
	for(int i=0; get_getsetter(i)->name; i++)
	    if(extra_info || get_getsetter(i)->name[0]!='_')
	    {
		if(to.tp_base)
		    gendata_string_as_cpp(((pymd_getset_closure*)to.tp_getset[i].closure)->data, doc, buffer, n_buffer);
		else
		    snprintf(buffer, n_buffer, "%s", get_getsetter(i)->name);
		printf("\t%*s%s\n", level*5+9, "", buffer);
	    }
    }
    if(has_methods())
    {
	printf(RED"%*sMethods:"RESET"\n", level*5, "");
	for(int i=0; get_method(i)->ml_name; i++)
	    if(extra_info || get_method(i)->ml_name[0]!='_')
	    {
		if(to.tp_base)
		{
		    int j;
		    for(j=0; j<method_closure[i+method_closure_first].n_poly; j++)
		    {
			gendata_string_as_cpp(method_closure[i+method_closure_first].poly[j].data, doc, buffer, n_buffer);
			printf("\t%*s%2d  %s\n", level*5+9, "",
			       i+method_closure_first, buffer);
		    }
		    if(!j) printf("\t%*s%2d  %s\n", level*5+9, "", i+method_closure_first, get_method(i)->ml_name);
		}
		else
		    printf("\t%*s%s\n", level*5+9, "", get_method(i)->ml_name);
	    }
    }

    // do inheritance recursion, but omitting last "object" python base class
    if(to.tp_base && (extra_info || to.tp_base->tp_base/*"object" base type has no inheritance*/))
    {
	printf(RED"%*sBase:"RESET"\n", level*5, "");
	((python_type_metadata*)to.tp_base)->dump(extra_info, level+1);
    }
}

void python_type_metadata::set_context_object_for_functions(python_type_metadata *ctx)
{
    functions_object_ctx=ctx;
}

PYMD_GENERATE(PYMD_PROTO_DEFINITION, 0);

PyObject *(*python_type_metadata::pyfunc[])(PyObject *self, PyObject *args)=
{
    PYMD_GENERATE(PYMD_PROTO_INITIALIZAT, 0)
    NULL
};

PyObject *python_type_metadata::pyfunc_resolver(int nfunc, PyObject *self, PyObject *args)
{
    python_type_metadata *type;

    if(!self)
    {
	// this should be a function (i.e. no class context)
	type=functions_object_ctx;
    }
    else
	type=(python_type_metadata *)self->ob_type;

    // go to corresponding base class up in inheritance tree
    while(nfunc<type->method_closure_first) type=(python_type_metadata *)type->to.tp_base;

    pymd_method_closure *closure=&type->method_closure[nfunc];

    // special function
    if(closure->internal_type!=PYMD_PYTHON_INTERNAL_SPECIAL_NONE)
    {
	switch(closure->internal_type)
	{
	case PYMD_PYTHON_INTERNAL_SPECIAL_UNARY:
	    {
		// the python proto for this is:  PyObject * (*unaryfunc)(PyObject *);
		// so convert it to args format
		args=NULL;
	    }
	    break;

	case PYMD_PYTHON_INTERNAL_SPECIAL_BINARY:
	case PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY:
	    {
		// the python proto for this is: typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);
		// so convert it to args format
		PyObject *tuple=PyTuple_New(1);
		Py_INCREF(args);
		PyTuple_SET_ITEM(tuple, 0, args);
		args=tuple;
	    }
	    break;

	case PYMD_PYTHON_INTERNAL_SPECIAL_INTARG:
	    {
		// the python proto for this is: typedef PyObject *(*intargfunc)(PyObject *, int);
		// so convert it to args format
		PyObject *tuple=PyTuple_New(1);
		PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong((int)args));
		args=tuple;
	    }
	    break;

	case PYMD_PYTHON_INTERNAL_SPECIAL_TERNARY:
	default:
	    fprintf(stderr, "internal: TODO: internal type %d unknown\n", closure->internal_type);
	    Py_RETURN_NONE;
	}
    }

    // search a matching function according to args
    int n_args=(args && PyTuple_Check(args))?PyTuple_Size(args):0;
    for(int i=0; i<closure->n_poly; i++)
    {
	int *pos_method=closure->poly[i].data;
	int n_args_poly=gendata_get_data(pos_method, GENDATA_GENDATA_VAR);
	if(n_args>n_args_poly) continue;

	int *pos_args_poly=pos_method+gendata_get_position(pos_method, GENDATA_GENDATA_VAR)+1;
	int n_stack_bytes=0;
	int n_artificial;
	for(n_artificial=0; n_artificial<n_args_poly; n_artificial++)
	{
	    if(!gendata_get_data(pos_args_poly, GENDATA_ARTIFICIAL)) break;
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	if(n_artificial>1)
	{
	    fprintf(stderr, "TODO: manage more than 1 (%d) artificial parameters\n", n_artificial);
	    continue;
	}
	n_stack_bytes+=sizeof(void*); // must be this
	if(n_args!=n_args_poly-n_artificial) continue;

	// check non-artificial params
	int match=1;
	for(int j=0; match && j<n_args; j++)
	{
	    PyObject *param=PyTuple_GetItem(args, j);
	    int *pos_md_type, size;
	    md_check_python_func func;
	    if(!(pos_md_type=gendata_resolve_pos_type(pos_args_poly)) ||
	       (size=gendata_get_data(pos_md_type, GENDATA_SIZE))<=0 ||
	       !(func=pymdg.get_func_check_python(*pos_md_type)) ||
	       !pymdg.get_func_python_to_c(*pos_md_type))
	    {
		fprintf(stderr, "internal: bad type in function params\n");
		match=0;
		break;
	    }
	    match=func(param, pos_md_type);
	    n_stack_bytes+=ROUND_UP(size);
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	if(!match) continue;

	// push params into stack
	if((unsigned)n_stack_bytes>MARSHALL_MAX_STACK*sizeof(int))
	{
	    fprintf(stderr, "internal: fatal, arguments size %d > static maximum size %d\n",
		    n_stack_bytes, MARSHALL_MAX_STACK*sizeof(int));
	    Py_RETURN_NONE;
	}

	// fill the stack
	int esp[MARSHALL_MAX_STACK];
	int i_esp=0;
	pos_args_poly=pos_method+gendata_get_position(pos_method, GENDATA_GENDATA_VAR)+1;
	for(int j=0; j<n_artificial; j++)
	{
	    esp[i_esp++]=(int)(((python_metadata*)self)->get_obj());
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	for(int j=n_artificial; j<n_args_poly; j++)
	{
	    PyObject *param=PyTuple_GetItem(args, j-n_artificial);
	    int *pos_md_type, size;
	    md_python_to_c_func func;
	    pos_md_type=gendata_resolve_pos_type(pos_args_poly);  // already checked
	    size=gendata_get_data(pos_md_type, GENDATA_SIZE);     // "
	    func=pymdg.get_func_python_to_c(*pos_md_type);        // "

	    // reset zone of the stack, just in case next function tries to free any pointer
	    memset(&esp[i_esp], 0, size);

	    if(func(param, &esp[i_esp], pos_md_type)==-1)
	    {
		fprintf(stderr, "internal: fatal, pushing the function stack (func:%d param:%d)\n", i, j);
		match=0;
		break;
	    }
	    i_esp+=ROUND_UP(size)>>2;
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	if(!match) continue;

	// virtual methods
	void *func_addr;
	if(closure->poly[i].vtable_pos>=0)
	{
	    // virtual method
	    void **vtable_ptr;
	    if(!(vtable_ptr=*(void***)esp[0])) ERROR_R0("internal: vtable_ptr is NULL! (possibly the constructor failed)");
	    func_addr=vtable_ptr[closure->poly[i].vtable_pos];
	}
	else
	{
	    // non virtual method
	    func_addr=closure->poly[i].func;
	}

	// return of special functions
	PyObject *pyreturn_val=NULL;
	if(closure->internal_type==PYMD_PYTHON_INTERNAL_SPECIAL_IBINARY)
	{
	    // call to void function and return self
	    marshalling::func_call_ret_void(esp, func_addr, i_esp);
	    Py_INCREF(self);
	    pyreturn_val=self;
	}
	else
	{
	    // call the function and pythonzie return value
	    int *return_type_pos;
	    marshalling_return_type return_marshalling;
	    md_free_c_func free_return;

	    if((return_type_pos=gendata_resolve_pos_type(pos_method)))
	    {
		return_marshalling=pymdg.get_return_marshalling_type(*return_type_pos);
		free_return=pymdg.get_func_free_c(*return_type_pos);
	    }
	    else
	    {
		return_marshalling=PYMD_MARSHALLING_RETURN_VOID;
		free_return=NULL;
	    }

	    switch(return_marshalling)
	    {
	    case PYMD_MARSHALLING_RETURN_VOID:
		marshalling::func_call_ret_void(esp, func_addr, i_esp);
		break;
	    case PYMD_MARSHALLING_RETURN_INT:
	    case PYMD_MARSHALLING_RETURN_INT_DONOTFREE:
		{
		    int ii=marshalling::func_call_ret_int(esp, func_addr, i_esp);
		    pyreturn_val=pymdg.get_func_c_to_python(*return_type_pos)(&ii, return_type_pos);
		    if(free_return && return_marshalling!=PYMD_MARSHALLING_RETURN_INT_DONOTFREE)
			free_return(&ii, return_type_pos);
		}
		break;
	    case PYMD_MARSHALLING_RETURN_LONG_LONG:
		{
		    long long l=marshalling::func_call_ret_long_long(esp, func_addr, i_esp);
		    pyreturn_val=pymdg.get_func_c_to_python(*return_type_pos)(&l, return_type_pos);
		    if(free_return) free_return(&l, return_type_pos);
		}
		break;
	    case PYMD_MARSHALLING_RETURN_FLOAT:
		{
		    float f=marshalling::func_call_ret_double(esp, func_addr, i_esp);
		    pyreturn_val=pymdg.get_func_c_to_python(*return_type_pos)(&f, return_type_pos);
		    if(free_return) free_return(&f, return_type_pos);
		}
		break;
	    case PYMD_MARSHALLING_RETURN_DOUBLE:
		{
		    double d=marshalling::func_call_ret_double(esp, func_addr, i_esp);
		    pyreturn_val=pymdg.get_func_c_to_python(*return_type_pos)(&d, return_type_pos);
		    if(free_return) free_return(&d, return_type_pos);
		}
		break;
	    default:
		fprintf(stderr, "internal: TODO: return value %s not implemented\n", gendata_type_strings[*return_type_pos]);
	    }
	}

	// free parameters
	pos_args_poly=pos_method+gendata_get_position(pos_method, GENDATA_GENDATA_VAR)+1;
	for(i_esp=0; i_esp<n_artificial; i_esp++) pos_args_poly=gendata_traverse(pos_args_poly);
	for(int j=n_artificial; j<n_args_poly; j++)
	{
	    // TODO: change reference params? :: PyObject *param=PyTuple_GetItem(args, j-n_artificial);
	    int *pos_md_type, size;
	    md_free_c_func func;
	    pos_md_type=gendata_resolve_pos_type(pos_args_poly);  // already checked
	    size=gendata_get_data(pos_md_type, GENDATA_SIZE);     // "
	    if((func=pymdg.get_func_free_c(*pos_md_type)) &&
	       !func(&esp[i_esp], pos_md_type))
		fprintf(stderr, "internal: error freeing function params (func:%d param:%d)\n", i, j);

	    i_esp+=ROUND_UP(size)>>2;
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}

	// check if this is a postunpickle function, if so, register event queue methods
	if(closure->poly[i].attr.postunpickle) type->register_event_queue_methods(self);

	// return
	if(pyreturn_val) return pyreturn_val; else Py_RETURN_NONE;
    }

    // no matching prototype found
    // generate gcc like prototype error with the alternatives
    char *func_name=&type->doc[gendata_get_data(closure->poly[0].data, GENDATA_NAME)];
    pymdg.clear_error_string();
    pymdg.add_error_string("previous error: \"%s\"\n", pymetadata_global::python_error_string());
    pymdg.add_error_string("cannot find prototype for %s function, candidates are:\n", func_name);
    for(int i=0; i<closure->n_poly; i++)
    {
	if(i) pymdg.add_error_string("\n");
	int *pos_method=closure->poly[i].data;
	int n_args_poly=gendata_get_data(pos_method, GENDATA_GENDATA_VAR);
	int *pos_args_poly=pos_method+gendata_get_position(pos_method, GENDATA_GENDATA_VAR)+1;
	int n_artificial;
	for(n_artificial=0; n_artificial<n_args_poly; n_artificial++)
	{
	    if(!gendata_get_data(pos_args_poly, GENDATA_ARTIFICIAL)) break;
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	pymdg.add_error_string("\t");
	int ret_type;
	if((ret_type=gendata_resolve_type(pos_method))>=0)
	    pymdg.add_error_string("%s ", gendata_type_strings[ret_type]);
	pymdg.add_error_string("%s(", func_name);
	for(int j=0; j<n_args_poly-n_artificial; j++)
	{
	    if(j) pymdg.add_error_string(", ");
	    int param_c_type=gendata_resolve_type(pos_args_poly);
	    pymdg.add_error_string("%s", gendata_type_strings[param_c_type]);
	    int idoc; char *name=(idoc=gendata_get_data(pos_args_poly, GENDATA_NAME))<0 ? NULL:&type->doc[idoc];
	    if(name) pymdg.add_error_string(" %s", name);
	    pos_args_poly=gendata_traverse(pos_args_poly);
	}
	pymdg.add_error_string(")");
    }
    PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
    return NULL;
}

int *python_type_metadata::get_data_of_member_at_location(int location)
{
    int n_meta=gendata_get_data(data, GENDATA_GENDATA_VAR);
    int *position=data+gendata_get_position(data, GENDATA_GENDATA_VAR)+1;
    for(int i=0; i<n_meta; i++)
    {
	switch(*position)
	{
	case GENDATA_MEMBER:
	case GENDATA_MEMBER_VECTOR_N_MAX:
	case GENDATA_MEMBER_OBJECT:
	case GENDATA_MEMBER_STD_VECTOR_IMPL:
	    if(location==gendata_get_data(position, GENDATA_LOCATION)) return position;
	}
	position=gendata_traverse(position);
    }

    // recursive
    if(to.tp_base && to.tp_base->tp_base/*object is always the top base*/)
	return ((python_type_metadata*)to.tp_base)->get_data_of_member_at_location(location);

    return NULL;
}

void *python_type_metadata::get_closure_of_member_with_data(int *data)
{
    for(int i=0; to.tp_getset[i].name; i++)
	if(((pymd_getset_closure *)to.tp_getset[i].closure)->data==data) return to.tp_getset[i].closure;
    return NULL;
}


CLASS_INIT(python_metadata); // PYTHON_METADATA CLASS

python_metadata::python_metadata()
{
    CLASS_CTOR(python_metadata);
}

python_metadata::~python_metadata()
{
    CLASS_DTOR(python_metadata);

    // check python refcnt
    if(ob_refcnt) ERROR("reference count (=%d) should be zero", ob_refcnt);
}

python_metadata *python_metadata::new_python_metadata(void *obj, int indirection, python_metadata *obj_container,
						      python_type_metadata *type, PyObject *ctor_args)
{
    if(!obj || !type) ERROR_R0("internal: w/o obj or type");

    // try to find an already defined python reference
    int i_destination;
    python_metadata *python_obj;
    if((python_obj=pymdg.search_object(obj, type, &i_destination)))
    {
	// reference already exists
	if(ctor_args) ERROR("warning: same reference but asked to execute ctor");

	python_obj->python_incref();
    }
    else
    {
	// polymorphism: specialization
	void **vtable;
	if(!ctor_args && (vtable=type->get_vtable()))
	{
	    void **obj_vtable;
	    if(!indirection) obj_vtable=*(void***)obj; else obj_vtable=**(void****)obj;
	    if(obj_vtable!=vtable)
	    {
		python_type_metadata *type_specialized;
		if((type_specialized=pymdg.search_type_by_vtable(obj_vtable)))
		{
		    // specialize object
		    type=type_specialized;

		    // try to find an already defined python reference: again
		    if((python_obj=pymdg.search_object(obj, type, NULL)))
		    {
			python_obj->python_incref();
			return python_obj;
		    }
		}
		else
		    ERROR("Polymorphed specialized C++ object type not found: %s", type->get_name());
	    }
	}

	// create new python object
	python_obj=new python_metadata;
	python_obj->ob_refcnt=1;
	python_obj->ob_type=(PyTypeObject*)type;
	if(!(python_obj->obj_container=obj_container))
	    python_obj->obj=obj;
	else
	{
	    void *contained_cobj;
	    if(!(contained_cobj=obj_container->get_obj()))
		ERROR_R0("procedural/internal: container obj w/o C++ obj");
	    python_obj->location=(int)obj-(int)contained_cobj;
	    python_obj->indirection=indirection;
	}

	// execute ctor
	if(ctor_args)
	{
	    // this object is a new one
	    bool executed_ctor=false;

	    if(type->i_ctor>=0)
	    {
		if(!type->pyfunc_resolver(type->i_ctor, (PyObject*)python_obj, ctor_args))
		{
		    // last chance to execute a constructor (default memset constructor)
		    if(!PyTuple_Check(ctor_args)) ERROR_R0("internal: fatal, ctor_args is not a tuple");
		    if(!PyTuple_Size(ctor_args))
		    {
			// OK, execute memset constructor, despite it's dangerous, present a warning
			memset(obj, 0, python_obj->get_c_size());
			fprintf(stderr, "Warning: no default ctor provided in class %s (perhaps due to linkage problems), object reset\n",
				type->get_name());
			PyErr_Clear();
		    }
		    else
		    {
			// constructor not called (perhaps because of no matching proto), object must be destroyed
			python_obj->ob_refcnt=0;
			delete python_obj;
			return NULL;
		    }
		}
		else executed_ctor=true;
	    }
	    else
	    {
		// no constructor, so check no params
		if(!PyTuple_Check(ctor_args)) ERROR_R0("internal: fatal, ctor_args is not a tuple");
		if(PyTuple_Size(ctor_args)!=0)
		{
		    pymdg.clear_error_string();
		    pymdg.add_error_string("class %s has no ctors but %d params provided", type->get_name(), PyTuple_Size(ctor_args));
		    PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
		    return NULL;
		}
	    }

	    // vtable before base ctors (that install their own vtable)
	    void **vtable_position=NULL;
	    if(type->vtable_position)
	    {
		if((vtable_position=*(void***)obj))
		{
		    if(vtable_position != type->vtable_position)
			ERROR_R0("internal: fatal, vtable position not in place %p!=%p", vtable_position, type->vtable_position);
		}
		else
		    vtable_position=type->vtable_position;
	    }

	    // execute base ctors
	    if(!executed_ctor)
	    {
		python_type_metadata *base=type;
		while(base->to.tp_base && base->to.tp_base->tp_base)
		{
		    base=(python_type_metadata *)base->to.tp_base;
		    if(base->i_ctor>=0 && base->pyfunc_resolver(base->i_ctor, (PyObject*)python_obj, NULL/*ctor_args*/))
		    {
			// ctor executed
			break; // when first one executed, the rest are executed in cascade
		    }
		}
	    }

	    // vtable
	    if(type->vtable_position) *(void**)obj=vtable_position;
	}
	else
	{
	    // this is not a new C++ object, so increase C++ its reference counter
	    if(!obj_container) python_obj->c_incref();
	}

	// register object
	if(pymdg.add_object(python_obj, i_destination)<0) ERROR("internal: creating new pyobj");

	// increase container reference counter
	if(obj_container) obj_container->python_incref();
    }

    return python_obj;
}

void python_metadata::delete_python_metadata(python_metadata *python_obj)
{
    if(!python_obj) ERROR_R("internal: w/o python obj");

    // deregister object
    if(!pymdg.remove_object(python_obj)) ERROR("internal: object not registered when trying to remove it");

    // decrease C++ object refcnt
    if(!python_obj->obj_container) python_obj->c_decref();

    // decrease container refcnt
    else python_obj->obj_container->python_decref();

    // call destructor
    delete python_obj;
}

python_metadata *python_metadata::python_decref()
{
    ob_refcnt--;
    if(ob_refcnt<0) ERROR("reference count below zero");
    else if(!ob_refcnt)
    {
	if(!ob_type) ERROR("no python_type to call dealloc function of type");
	else ob_type->tp_dealloc((PyObject*)this);
	return NULL;
    }
    return this;
}

int python_metadata::has_refcnt()
{
    return (!obj_container && ((python_type_metadata*)ob_type)->refcnt_location!=GENDATA_INVALID_VALUE);
}

int python_metadata::get_refcnt()
{
    int location;
    char *cobj;
    if(!obj_container && (cobj=(char*)get_obj()) && (location=((python_type_metadata*)ob_type)->refcnt_location)!=GENDATA_INVALID_VALUE)
	return *(int*)(cobj+location);
    else
	return -1;
}

void python_metadata::c_incref()
{
    if(obj_container) return;
    void *cobj;

    // check
    if(!(cobj=get_obj())) ERROR("internal: python object host a NULL C++ object");

    c_incref(cobj, (python_type_metadata *)ob_type);
}

void python_metadata::c_incref(void *obj, python_type_metadata *type)
{
    int location;
    if(obj && (location=type->refcnt_location)!=GENDATA_INVALID_VALUE)
    {
	++*(int*)((char*)obj+location);
#if 0
	// debug
	ERROR("C++ INC: %p PostCNT: %d", obj, *(int*)((char*)obj+location));
#endif
    }
}

void *python_metadata::c_decref()
{
    if(obj_container) return NULL;
    void *cobj;

    // check
    if(!(cobj=get_obj())) ERROR("internal: python object host a NULL C++ object");

    return obj=c_decref(obj, (python_type_metadata *)ob_type);
}

void *python_metadata::c_decref(void *obj, python_type_metadata *type)
{
    if(obj)
    {
	int location;
	if((location=type->refcnt_location)==GENDATA_INVALID_VALUE || !(--*(int*)((char*)obj+location)))
	{
#if 0
	    // debug
	    ERROR("C++ DEC: %p Object Deleted", obj);
#endif

	    // delete C++ object
	    type->cdealloc(obj);
	    return NULL;
	}
#if 0
	// debug
	ERROR("C++ DEC: %p PostCNT: %d", obj, *(int*)((char*)obj+location));
#endif
    }
    return obj;
}

void *python_metadata::get_obj()
{
    // if free obj, then obj points to C++ obj
    if(!obj_container) return obj;

    // obtain C++ object pointer of container
    void *container_obj;
    if(!(container_obj=obj_container->get_obj())) return NULL; //ERROR_R0("python container object w/o C++ object");
    container_obj=(char*)container_obj+location;

    // take indirection into account
    if(indirection)
    {
	char *indirection=*(char**)container_obj;
	if(indirection) return indirection;
	else return NULL; //ERROR_R0("python container reference vanished");
    }
    else
	return container_obj;
}

void *python_metadata::get_obj_woind()
{
    // if free obj, then obj points to C++ obj
    if(!obj_container) return obj;

    // obtain C++ object pointer of container
    void *container_obj;
    if(!(container_obj=obj_container->get_obj())) return NULL; //ERROR_R0("python container object w/o C++ object");
    return (char*)container_obj+location;
}

pymd_cobj_type python_metadata::get_obj_type()
{
    if(!obj_container) {if(has_refcnt()) return PYMD_P1; else return PYMD_P2;}
    else {if(indirection) return PYMD_P4; else return PYMD_P3;}
}

int python_metadata::get_c_size()
{
    python_type_metadata *type=(python_type_metadata*)ob_type;
    if(type==pymdg.get_type_python_vector())
    {
	if(!obj_container) return 0;

	// use data from container
	int n, size, *pos_type_metadata;
	if(!python_vector::get_info(this, &n, &size, &pos_type_metadata)) return 0;
	return n*size;
    }
    return gendata_get_data(type->data, GENDATA_SIZE);
}

void python_metadata::transmute_object_type(unsigned indirection_, unsigned location_, python_metadata *obj_container_)
{
    obj_container=obj_container_;
    indirection=indirection_;
    location=location_;
}

void python_metadata::transmute_object_type(void *obj_)
{
    obj_container=NULL;
    obj=obj_;
}

bool python_metadata::check_vanished()
{
    if(!get_obj())
    {
	pymdg.clear_error_string();
	pymdg.add_error_string("C/C++ structure/object of type %s vanished", get_type_string());
	PyErr_SetString(PyExc_TypeError, pymdg.get_error_string());
	return true;
    }
    return false;
}
